package datagraph.core

import java.util.UUID

import datagraph.bundle.{PrintAndOutput, Read}
import datagraph.core.Bundle.Inputable
import ws.very.util.lang.ContextRegiser
import ws.very.util.lang.Reflects

import scala.reflect.ClassTag


trait GraphConf //[T <: DataGraph]
  extends ContextRegiser.Base[BundleConf[_]]
    /*   with
     ContextRegiser.AutoReg[BundleConf[_]]  */ {

  final def getAllRegisBundles = getRegs

}


object GraphConf {

  trait Basic extends GraphConf {
    @deprecated
    protected type Bundle[T <: datagraph.core.Bundle] = BundleConf.BasicInputCtl[T]

    def conf[T <: datagraph.core.Bundle](b: => T) = new BundleConf.BasicInputCtl[T] {

      def bundle: T = b
    }
  }

  trait Cacheable {
    def defaultEnableCache = true

    import scala.concurrent.duration._

    protected var expireTime = 30 day
  }

  trait CacheAny extends GraphConf with Cacheable {

    protected type Bundle[T <: datagraph.core.Bundle] = BundleConf.BasicInputCtl[T]

    def conf[T <: datagraph.core.Bundle](b: => T) = new BundleConf.BasicInputCtl[T] {

      def bundle: T = b
    }

    def save(bundleLink: Cache.CacheAnyBundleLink, result: Any)

    def get(bundleLink: Cache.CacheAnyBundleLink): Any

  }


  trait CacheLink extends GraphConf with Cacheable {

    protected type Bundle[T <: datagraph.core.Bundle.Outputable] = BundleConf.OutputCacheLinkCtl[T]

    def conf[T <: Bundle.NotOutputable](b: => T) = new BundleConf.CacheLinkCtl[T] {

      def bundle: T = b
    }

    // type Output<:Bundle.OutPutable2

    def conf[B <: Bundle.Outputable](b: => B)(implicit resultRW: ReadWriter[B#Out], bundleRW: ReadWriter[B]) = {
      val t = new BundleConf.OutputCacheLinkCtl[B] {
        override def bundle = b

        override val resultReadWriter: ReadWriter[B#Out] = resultRW //implicitly[ReadWriter[Out]]

        override val bundleReadWriter: ReadWriter[B] = bundleRW //implicitly[ReadWriter[B]]
      }
      t
    }


    /*    def confOnlyOutput[Out: ReadWriter,B<:Bundle.OnlyOutput[Out]:ReadWriter](b:B ) = {
          val t = new BundleConf.OutputCacheLinkCtl[Out,Bundle.OnlyOutput[Out]] {
            override def bundle = b

            override val resultReadWriter: ReadWriter[Out] = implicitly[ReadWriter[Out]]
          }
          t
        }

        def confInAndOut[In, Out: ReadWriter,B<:Bundle.InAndOut[In, Out]:ReadWriter](b: B) = {
          val t = new BundleConf.OutputCacheLinkCtl[Out,Bundle.InAndOut[In, Out]] {
            override def bundle = b

            override val resultReadWriter: ReadWriter[Out]=implicitly[ReadWriter[Out]]
          }
          val tmp=implicitly[ReadWriter[Out]]
          t
        }*/


    def save(bundleLink: Cache.CacheTypeBundleLink, result: Any)

    def get(bundleLink: Cache.CacheTypeBundleLink): Any
  }


}

/*trait TargetGraphConf extends GraphConf{
  def targets: Iterable[BundleConf[_ <: Bundle.Outputable[_]]]
}*/


object BundleConf {


  trait BasicInputCtl[+T <: Bundle] extends BundleConf[T] {
    self =>
    protected var inputProcess: (() => Seq[InSourceConf], Seq[Any] => Any) = _

    def inputProcesses = inputProcess


    protected class inputCtl[Param] {
      //
      def one[In](inSources: => InSourceConf, getFn: In => Param = { r: In => r.asInstanceOf[Param] }
                 ): BundleConf[T] = {
        inputProcess = (() => Seq(inSources.asInstanceOf[InSourceConf]), { rs => getFn(rs.head.asInstanceOf[In]) })
        self
      }

      /* def two[In, In2](inSources: =>(InSourceConf[In], InSourceConf[In2]))(
         getFn: (In, In2) => Param): BundleConf[T] = {

         inputProcess = (Seq(inSources._1, inSources._2).map {
           _.asInstanceOf[InSourceConf[_]]
         }, { rs => getFn(rs.head.asInstanceOf[In], rs(2).asInstanceOf[In2]) })
         self
       }

       def three[In, In2, In3](inSources: =>(InSourceConf[In], InSourceConf[In2], InSourceConf[In3]))(
         getFn: (In, In2, In3) => Param): BundleConf[T] = {

         inputProcess = (Seq(inSources._1, inSources._2, inSources._3).map {
           _.asInstanceOf[InSourceConf[_]]
         }, { rs => getFn(rs.head.asInstanceOf[In], rs(2).asInstanceOf[In2], rs(3).asInstanceOf[In3]) })
         self
       }*/

      def apply(inSources: => Seq[InSourceConf])(
        getFn: Seq[Any] => Param): BundleConf[T] = {

        inputProcess = (() => inSources, getFn)
        self
      }
    }


    //protected[BundleConf]
    def input[Param](implicit t: T <:< Inputable {
      type In = Param
    }) = new inputCtl[Param]
  }

  trait Cache[+T <: Bundle] extends BundleConf[T] {
    self =>
    def enableCache = true
  }

  trait BasicWithCache[+T <: Bundle] extends Cache[T] with BasicInputCtl[T]

  trait CacheLinkCtl[+T <: Bundle] extends Cache[T] with BasicInputCtl[T]{
    self =>

/*    protected var inputProcess: (() => Seq[InSourceConf], Seq[Any] => Any) = _

    def inputProcesses = inputProcess


    def input[Param](implicit t: T <:< Inputable {
      type In = Param
    }) = new inputCtl[Param]

    protected class inputCtl[Param] {
      //
      /*     def one[In: ReadWriter](inSources: => InSourceConf, getFn: In => Param = { r: In => r.asInstanceOf[Param] }
                                  ): BundleConf[T] = {
             val rw = implicitly[ReadWriter[In]]
             // inputProcess = (() => Seq(inSources.asInstanceOf[InSourceConf[_]]), { rs => getFn(rs.head.asInstanceOf[In]) })

             self
           }*/
      def one[In](inSources: => InSourceConf, getFn: In => Param = { r: In => r.asInstanceOf[Param] }
                 ): BundleConf[T] = {
        inputProcess = (() => Seq(inSources.asInstanceOf[InSourceConf]), { rs => getFn(rs.head.asInstanceOf[In]) })
        self
      }
    }*/

  }

  trait OutputCacheLinkCtl[B <: Bundle.Outputable] extends CacheLinkCtl[B] {
    def resultReadWriter: ReadWriter[B#Out]

    def bundleReadWriter: ReadWriter[B]
  }

}

trait BundleConf[+T <: Bundle] {
  self =>

  type InSource = Bundle.Outputable

  type InSourceConf = BundleConf[InSource]


  //type InSources <: Product


  def bundle: T


  var enable = true
}

/*class BundleConf[T <: Bundle : ClassTag] extends BundleConfBase[T] {


  //def inputPort[R](implicit t: Self <:< Bundle.Outputable[_] ) = ???

  /*  def input[InputVal,InputConf,InputResult](inputConf:InputConf,inputFn:InputVal=>InputResult)
                                             (implicit t: T <:<  Bundle.Outputable[_]) = ???*/

  def input(implicit t: T <:< Bundle.Inputable[_]) = ???

   def build = ???
}*/

/*object BundleConf{
  class Outputable[ T <: Bundle.Outputable[_] : ClassTag]extends BundleConfBase[T] {
    override val clazz: Class[T] = implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]]

  }
}*/

